/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2009 Xilinx, Inc. All Rights Reserved.
//
// You may copy and modify these files for your own internal use solely with
// Xilinx programmable logic devices and  Xilinx EDK system or create IP
// modules solely for Xilinx programmable logic devices and Xilinx EDK system.
// No rights are granted to distribute any files unless they are distributed in
// Xilinx programmable logic devices.
//
/////////////////////////////////////////////////////////////////////////////////

/*
 *      Simple SREC Bootloader
 *      This simple bootloader is provided with Xilinx EDK for you to easily re-use in your
 *      own software project. It is capable of booting an SREC format image file 
 *      (Mototorola S-record format), given the location of the image in memory.
 *      In particular, this bootloader is designed for images stored in non-volatile flash
 *      memory that is addressable from the processor. 
 *
 *      Please modify the define "FLASH_IMAGE_BASEADDR" in the blconfig.h header file 
 *      to point to the memory location from which the bootloader has to pick up the 
 *      flash image from.
 *
 *      You can include these sources in your software application project in XPS and 
 *      build the project for the processor for which you want the bootload to happen.
 *      You can also subsequently modify these sources to adapt the bootloader for any
 *      specific scenario that you might require it for.
 *
 */


#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "xspi.h"
#include "srec.h"
#include "flash.h"
#include "xparameters.h"

#define VERBOSE

#define rXNS_DELAY      (*(volatile unsigned int *)(XPAR_FUSION_CTRL_0_S_AXI_RNG00_BASEADDR + 44)) //;
#define udelay(x)       rXNS_DELAY = x*100; while(rXNS_DELAY > 0)

uint8_t rv_buf[SREC_MAX_BYTES + 4];

static uint8_t sr_buf[SREC_MAX_BYTES];
static uint8_t sr_data_buf[SREC_DATA_MAX_BYTES];

static XSpi Spi;
static srec_info_t srinfo;

uint32_t fl_addr;

static int spi_flash_get_id(void){
	u32 id;

	rv_buf[0] = 0x90;
	XSpi_Transfer(&Spi, rv_buf, rv_buf, 6);
	id = rv_buf[4]<<8|rv_buf[5];

	return id;
}

static uint8_t mem_get_srec_line (uint8_t *buf,uint8_t *mem_ptr)
{
    uint8_t c;
    int count = 0;

    while (1) {
        c  = mem_ptr[fl_addr];
        fl_addr++;
        if (c == 0xD) {
            /* Eat up the 0xA too */
            fl_addr++;
            return 0;
        }

        *buf++ = c;
        count++;
        if (count > SREC_MAX_BYTES)
            return LD_SREC_LINE_ERROR;
    }
}

#ifdef VERBOSE
static void display_progress (uint32_t count)
{
    /* Send carriage return */
    outbyte (CR);
    print  ("Bootloader: Processed (0x)");
    putnum (count);
    print (" S-records");
    outbyte (CR);
}
#else
	#define display_progress(x)
#endif


static uint8_t flash_get_srec_line (uint8_t *buf)
{
    uint8_t c;
    int count = 0;

    while (1) {
    	spi_flash_read(&fl_addr,1);
        c  = rv_buf[4];
        fl_addr++;
        if (c == 0xD) {
            /* Eat up the 0xA too */
            fl_addr++;
            return 0;
        }

        *buf++ = c;
        count++;
        if (count > SREC_MAX_BYTES)
            return LD_SREC_LINE_ERROR;
    }
}

void flash_init()
{
	uint32_t id;
	/*
	 * Initialize the SPI controller in polled mode
	 * and enable the flash slave select
	 */
	XSpi_Initialize(&Spi, SPI_DEVICE_ID);
	XSpi_SetOptions(&Spi, XSP_MASTER_OPTION | XSP_MANUAL_SSELECT_OPTION);
	XSpi_SetSlaveSelect(&Spi, SPI_FLASH_SLAVE_SELECT);
	XSpi_Start(&Spi);
	XSpi_IntrGlobalDisable(&Spi);

	id = spi_flash_get_id();
	xil_printf("Flash ChipID@ 0x%x\r\n",id);
}

void flash_sector_erase(uint32_t* FlashAddress) // erase 64KB
{
	uint8_t *Address = FlashAddress;

	rv_buf[0] = 0x06;
	XSpi_Transfer(&Spi, rv_buf, rv_buf, 1);

	rv_buf[0] = 0x20;
	rv_buf[1] = Address[2];
	rv_buf[2] = Address[1];
	rv_buf[3] = 0;
	XSpi_Transfer(&Spi, rv_buf, rv_buf, 4);

	rv_buf[1] = 1;
	while((rv_buf[1]&0x1) == 0x1){
		udelay(10000);
		rv_buf[0] = 0x05;
		XSpi_Transfer(&Spi, rv_buf, rv_buf, 2);
	}
}

void flash_page_write(uint32_t* FlashAddress, uint8_t* buf)  //write 256 Byte
{
	uint8_t *Address = FlashAddress;

	rv_buf[0] = 0x06;
	XSpi_Transfer(&Spi, rv_buf, rv_buf, 1);

	rv_buf[0] = 0x02;
	rv_buf[1] = Address[2];
	rv_buf[2] = Address[1];
	rv_buf[3] = 0;
	memcpy(&rv_buf[4],buf,256);

	XSpi_Transfer(&Spi, rv_buf, rv_buf, 256 + 4);

	rv_buf[1] = 1;
	while((rv_buf[1]&0x1) == 0x1){
		udelay(10000);
		rv_buf[0] = 0x05;
		XSpi_Transfer(&Spi, rv_buf, rv_buf, 2);
	}
}

int spi_flash_read(uint32_t* FlashAddress,uint32_t ByteCount)
{
	uint8_t *Address = FlashAddress;

	rv_buf[0] = 0x03;
	rv_buf[1] = Address[2];
	rv_buf[2] = Address[1];
	rv_buf[3] = Address[0];

	return XSpi_Transfer(&Spi, rv_buf, rv_buf, ByteCount + 4);
}


uint8_t load_exec ()
{
    uint8_t ret;
    void (*laddr)();
    int8_t done = 0;
    
    srinfo.sr_data = sr_data_buf;

    while (!done) {
        if ((ret = flash_get_srec_line (sr_buf)) != 0) return ret;
        if ((ret = decode_srec_line (sr_buf, &srinfo)) != 0) return ret;

        display_progress(srec_line);

        switch (srinfo.type) {
            case SREC_TYPE_0:
                break;
            case SREC_TYPE_1:
            case SREC_TYPE_2:
            case SREC_TYPE_3:
                memcpy ((void*)srinfo.addr, (void*)srinfo.sr_data, srinfo.dlen);
                break;
            case SREC_TYPE_5:
                break;
            case SREC_TYPE_7:
            case SREC_TYPE_8:
            case SREC_TYPE_9:
                laddr = (void (*)())srinfo.addr;
                done = 1;
                ret = 0;
                break;
        }
    }
    (*laddr)();                 
  
    /* We will be dead at this point */
    return 0;
}

uint8_t load_mem (uint8_t *mem_addr)
{
    uint8_t ret;
    void (*laddr)();
    int8_t done = 0;
    uint8_t *mem_ptr = mem_addr;

    srinfo.sr_data = sr_data_buf;

    while (!done) {
        if ((ret = mem_get_srec_line (sr_buf,mem_ptr)) != 0) return ret;
        if ((ret = decode_srec_line (sr_buf, &srinfo)) != 0) return ret;

        display_progress(srec_line);

        switch (srinfo.type) {
            case SREC_TYPE_0:
                break;
            case SREC_TYPE_1:
            case SREC_TYPE_2:
            case SREC_TYPE_3:
                memcpy ((void*)srinfo.addr, (void*)srinfo.sr_data, srinfo.dlen);
                break;
            case SREC_TYPE_5:
                break;
            case SREC_TYPE_7:
            case SREC_TYPE_8:
            case SREC_TYPE_9:
                laddr = (void (*)())srinfo.addr;
                done = 1;
                ret = 0;
                break;
        }
    }
    (*laddr)();

    /* We will be dead at this point */
    return 0;
}
